package client.logic.chat;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.PublicKey;

import packets.Packet;

import client.gui.frmChatFrame;
import client.logic.Controller;
import client.logic.Facade;
import encription.SecurityHandler;

/**
 * Class to receive chat requests
 */
public class ChatWaiter extends Thread implements Chatter{

	private ServerSocket _chatServerSocket;
	private ChatSender _chatSender;
	private ChatReciever _chatReciever;
	private Socket _sock;
	private PrivateKey _decryptionKey;
	private PublicKey _encryptionKey;
	private PublicKey _serverKey;
	private Controller _controller;

	/**
	 * Creates an instance of ChatWaiter
	 * @param decryptionKey the key to decrypt received messages with
	 * @param serverKey key to verify the server certificate
	 */
	public ChatWaiter(PrivateKey decryptionKey, PublicKey serverKey, Controller controller){
		_decryptionKey = decryptionKey;
		_serverKey = serverKey;
		_controller = controller;
	}

	/**
	 * runs the chat waiter thread
	 */
	public void run(){
		try {
			_chatServerSocket = new ServerSocket(DEFAULT_CHAT_PORT);
			System.out.println("waiting for chat");
			_sock = _chatServerSocket.accept();															//waits for a connection
			System.out.println("got chat request");
			InputStream _in = new BufferedInputStream(_sock.getInputStream());							//sets up an input stream to receive the connecting client's certificate
			byte[] buf = new byte[2048];																//buffer for the certificate
			int bytesRecieved =  _in.read(buf);															//receive data from foreign client
			Packet p = (Packet)SecurityHandler.getObject(SecurityHandler.decrypt(buf,_decryptionKey, bytesRecieved));	//decrypt the message and retrieve the packet
			_encryptionKey = p.getCertificateContent().getData(_serverKey).getKey();					//verify the certificate and get the public encryption key for the other client
			Packet verifyPacket = _controller.verifyKey(_encryptionKey);
			if (verifyPacket.getType() != Packet.OK){
				throw new InvalidKeyException("CRL: not a valid key");
			}
			//TODO get friends name
			frmChatFrame cf = new frmChatFrame(this , "");													//open a chat frame
			cf.setVisible(true);
			_chatReciever = new ChatReciever(_sock , cf, _decryptionKey);								//initiate a chat receiver
			_chatReciever.start();
			_chatSender = new ChatSender(_sock , Facade.getInstance().getLoggedUser(), _encryptionKey);	//initiate a chat sender
			_chatSender.init();																			//initialize the chat sender
		} catch (IOException e){
			//probably closed connection
		} catch (Exception e){
			close();
			reset();
			SecurityHandler.catchException("waiting for chats", e , true);
			//e.printStackTrace();
		}
	}

	/**
	 * sends a chat message to the foreign client
	 * @param message the message to send
	 */
	public void sendChatMessage(String message) {
		if (_chatSender!=null){
			_chatSender.sendMessage(message);
		}
	}

	/**
	 * closes this chat instance
	 */
	public void close(){
		try {
			if (_chatSender!=null)
				_chatSender.close();
			if (_chatReciever!=null)
				_chatReciever.close();
			if (_chatServerSocket!=null)
				_chatServerSocket.close();
		} catch (IOException e) {
			SecurityHandler.catchException("closing chat waiter" , e, true);
			//e.printStackTrace();
		}
	}

	/**
	 * resets the chat waiter
	 */
	public void reset(){
		Facade.getInstance().resetChatWaiter();
	}
}
